// Thomas Radloff, http://firecloud.com, 2008 

package {
  import flash.display.Sprite;
  import flash.events.Event;
  
  import org.papervision3d.cameras.Camera3D;
  import org.papervision3d.core.geom.Lines3D;
  import org.papervision3d.core.geom.renderables.Line3D;
  import org.papervision3d.core.geom.renderables.Vertex3D;
  import org.papervision3d.core.render.data.RenderStatistics;
  import org.papervision3d.lights.PointLight3D;
  import org.papervision3d.materials.WireframeMaterial;
  import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
  import org.papervision3d.materials.special.LineMaterial;
  import org.papervision3d.materials.utils.MaterialsList;
  import org.papervision3d.objects.DisplayObject3D;
  import org.papervision3d.objects.primitives.Cube;
  import org.papervision3d.render.BasicRenderEngine;
  import org.papervision3d.scenes.Scene3D;
  import org.papervision3d.view.Viewport3D;
  

  public class PPView extends Sprite {


    public function PPView() {
      // Initialise Papervision3D
      init3D();

      // Create the 3D objects
      createScene();

      // Initialise Event loop
      this.addEventListener(Event.ENTER_FRAME, onEnterFrame);    

    }

    // This function will create a set of RGB axes
    // lines joined at the given origin with end points
    // in the positive directions of posX, posY, and posZ
    // Note: There is no error checking to ensure the lines are
    // are all at 90 degree angles from each other
    private function createAxes(
      origin:Vertex3D,
      posX:Vertex3D,
      posY:Vertex3D,
      posZ:Vertex3D
      ):Lines3D {

      var defaultMaterial:LineMaterial = new LineMaterial(0xFFFFFF);
      var axes:Lines3D = new Lines3D(defaultMaterial);

      // Create a different color line material for each axis
      var xAxisMaterial:LineMaterial = new LineMaterial(0xFF0000);
      var yAxisMaterial:LineMaterial = new LineMaterial(0x00FF00);
      var zAxisMaterial:LineMaterial = new LineMaterial(0x0000FF);

      var xAxis:Line3D = new Line3D(axes, xAxisMaterial, 2, origin, posX);
      var yAxis:Line3D = new Line3D(axes, yAxisMaterial, 2, origin, posY);
      var zAxis:Line3D = new Line3D(axes, zAxisMaterial, 2, origin, posZ);

      // Add lines to the Lines3D container
      axes.addLine(xAxis);
      axes.addLine(yAxis);
      axes.addLine(zAxis);

      return axes;
    }

    private function createScene():void {

      // Specify a point light source and its location
      light = new PointLight3D(true);
      light.x = scale * 3.0;
      light.y = scale * 3.0;
      light.z = -scale * 4.0;
      scene.addChild(light);

      var fsm:FlatShadeMaterial =
        new FlatShadeMaterial(light,0xcccccc);

      // Do not lump these together into a subclass
      // because that will confuse the issue of
      // how transformation _really_ works
      cubeInner = 
        new Cube(
          new MaterialsList( {all:fsm}),
          scale, scale, scale
        );

      cubeInnerAxes = 
        createAxes(
          new Vertex3D(0,0,0),
          new Vertex3D(scale * 1.0, 0, 0),
          new Vertex3D(0, scale * 1.0, 0),
          new Vertex3D(0, 0, scale * 1.0)
        );

      var wfm:WireframeMaterial =
        new WireframeMaterial();

      cubeOuter = 
        new Cube(
          new MaterialsList( {all:wfm}),
          scale*2.0, scale*2.0, scale*2.0
        );

      cubeOuterAxes = 
        createAxes(
          new Vertex3D(0,0,0),
          new Vertex3D(scale * 1.0, 0, 0),
          new Vertex3D(0, scale * 1.0, 0),
          new Vertex3D(0, 0, scale * 1.0)
        );

      cubeOuter.addChild(cubeInner);
      cubeOuter.addChild(cubeInnerAxes);

      scene.addChild(cubeOuter);
      scene.addChild(cubeOuterAxes);

      axes = createAxes(
        new Vertex3D(0,0,0),
        new Vertex3D(scale * 1.0, 0, 0),
        new Vertex3D(0, scale * 1.0, 0),
        new Vertex3D(0, 0, scale * 1.0)
      );

      scene.addChild(axes);
    }

    private function init3D():void {

      // create viewport.
      // if autoScaleToStage = false (the default value)
      // then you can put the PPV image inside another
      // Flex component, such as a canvas
      viewport = new Viewport3D(355,355);
      addChild(viewport);

      // Create new camera with fov of 60 degrees (= default value)
      camera = new Camera3D(60);
      camera.x = scale * 2.0;
      camera.y = scale * 2.0
      camera.z = -scale * 10;

      // target camera on origin
      camera.target = DisplayObject3D.ZERO;

      // Create a new scene where our 3D objects will be displayed
      scene = new Scene3D();
  
      // Create new renderer
      renderer = new BasicRenderEngine();
    }

    private function onEnterFrame(event:Event):void {

      // Turn the axes on or off
      axes.visible = fShowAxes;

      // Render the 3D scene
      var rs:RenderStatistics;
      rs = renderer.renderScene(scene, camera, viewport);
    }

    // The four fundamental variables we need to
    // do PPV
    private var scene:Scene3D;
    private var camera:Camera3D;
    private var viewport:Viewport3D;
    private var renderer:BasicRenderEngine;

    // How large are the object in this model?
    // make them relative to scale
    private var scale:int = 100;

    // Show axes or not?
    public var fShowAxes:Boolean;
    private var axes:Lines3D;

    // The light
    private var light:PointLight3D;

    // The cubes
    // Do NOT try to lump these together into a subclass
    // because doing so would confuse the issue of how transformations
    // work
    public var cubeInner:Cube;
    public var cubeInnerAxes:Lines3D;

    public var cubeOuter:Cube;
    public var cubeOuterAxes:Lines3D;
  }
}